﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


namespace Tum.CollabXT
{
    /// <summary>
    /// Collection of helper functions. Injected into IProcessProvider via extension methods.
    /// </summary>
    public static class AssociationTools
    {
        /// <summary>
        /// Gets a product by its id.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="id">Id of the product to be returned.</param>
        /// <returns>Product having the passed id or null if it does not exist.</returns>
        public static IProduct GetProductByID(this IProcessProvider pp, string id)
        {
            return GetProcessObjectByID<IProduct>(id, pp.Products);
        }

        /// <summary>
        /// Gets an activity by its id.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="id">Id of the activity to be returned.</param>
        /// <returns>Activity having the passed id or null if it does not exist.</returns>
        public static IActivity GetActivityByID(this IProcessProvider pp, string id)
        {
            return GetProcessObjectByID<IActivity>(id, pp.Activities);
        }

        /// <summary>
        /// Gets a task by its id.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="id">Id of the task to be returned.</param>
        /// <returns>Task having the passed id or null if it does not exist.</returns>
        public static ITask GetTaskByID(this IProcessProvider pp, string id)
        {
            return GetProcessObjectByID<ITask>(id, pp.Tasks);
        }

        /// <summary>
        /// Gets a milestone by its id.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="id">Id of the milestone to be returned.</param>
        /// <returns>Milestone having the passed id or null if it does not exist.</returns>
        public static IMilestone GetMilestoneByID(this IProcessProvider pp, string id)
        {
            return GetProcessObjectByID<IMilestone>(id, pp.Milestones);
        }

        /// <summary>
        /// Gets a topic by its id.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="id">Id of the topic to be returned.</param>
        /// <returns>Topic having the passed id or null if it does not exist.</returns>
        public static ITopic GetTopicByID(this IProcessProvider pp, string id)
        {
            return GetProcessObjectByID<ITopic>(id, pp.Topics);
        }

        /// <summary>
        /// Gets a discipline by its id.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="id">Id of the discipline to be returned.</param>
        /// <returns>Discipline having the passed id or null if it does not exist.</returns>
        public static IDiscipline GetDisciplineByID(this IProcessProvider pp, string id)
        {
            return GetProcessObjectByID<IDiscipline>(id, pp.Disciplines);
        }

        /// <summary>
        /// Gets a role by its id.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="id">Id of the role to be returned.</param>
        /// <returns>Role having the passed id or null if it does not exist.</returns>
        public static IRole GetRoleByID(this IProcessProvider pp, string id)
        {
            return GetProcessObjectByID<IRole>(id, pp.Roles);
        }

        private static T GetProcessObjectByID<T>(string id, List<T> objList) where T : class, IProcessElement
        {
            foreach (T curObj in objList)
            {
                if (curObj.Id.Equals(id, StringComparison.OrdinalIgnoreCase))
                    return curObj;
            }
            return null;
        }


        /// <summary>
        /// Gets all responsible roles by a product id.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="productID">Product id.</param>
        /// <returns>Array of roles that are responsible for the product having the passed id or null if the product does not exist.</returns>
        public static IRole[] GetRolesResponsibleByProductID(this IProcessProvider pp, string productID)
        {
            return GetRolesByProductID<RoleResponsibleForProductDependency>(pp, productID);
        }

        /// <summary>
        /// Gets all participating roles by a product id.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="productID">Product id.</param>
        /// <returns>Array of roles that are participating in the product having the passed id or null if the product does not exist.</returns>
        public static IRole[] GetRolesParticipatingByProductID(this IProcessProvider pp, string productID)
        {
            return GetRolesByProductID<RoleParticipatingInProductDependency>(pp, productID);
        }

        private static IRole[] GetRolesByProductID<T>(IProcessProvider pp, string productID) where T : RoleToProductDependency
        {
            if (pp.GetProductByID(productID) == null)
                return null;

            List<IRole> responsibleRoles = new List<IRole>();
            foreach (IProcessElementDependency curDep in pp.Dependencies)
            {
                if (curDep is T)
                {
                    if ((curDep as T).ProductId.Equals(productID, StringComparison.OrdinalIgnoreCase))
                    {
                        IRole curRole = pp.GetRoleByID((curDep as T).RoleId);
                        if (curRole != null)
                            responsibleRoles.Add(curRole);
                    }
                }
            }
            return responsibleRoles.ToArray<IRole>();
        }

        /// <summary>
        /// Gets all products that a certain role is responsible for.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="roleID">Responsible role id.</param>
        /// <returns>Array of products that the passed role is responsible for or null if the role does not exist.</returns>
        public static IProduct[] GetProductsByResponsibleRoleID(this IProcessProvider pp, string roleID)
        {
            return GetProductsByRoleID<RoleResponsibleForProductDependency>(pp, roleID);
        }

        /// <summary>
        /// Gets all products that a certain role is participating in.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="roleID">Participating role id.</param>
        /// <returns>Array of products that the passed role is participating in or null if the role does not exist.</returns>
        public static IProduct[] GetProductsByParticipatingRoleID(this IProcessProvider pp, string roleID)
        {
            return GetProductsByRoleID<RoleParticipatingInProductDependency>(pp, roleID);
        }

        private static IProduct[] GetProductsByRoleID<T>(IProcessProvider pp, string roleID) where T : RoleToProductDependency
        {
            if (pp.GetRoleByID(roleID) == null)
                return null;

            List<IProduct> products = new List<IProduct>();
            foreach (IProcessElementDependency curDep in pp.Dependencies)
            {
                if (curDep is T)
                {
                    if ((curDep as T).RoleId.Equals(roleID, StringComparison.OrdinalIgnoreCase))
                    {
                        IProduct curProduct = pp.GetProductByID((curDep as T).ProductId);
                        if (curProduct != null)
                            products.Add(curProduct);
                    }
                }
            }
            return products.ToArray<IProduct>();
        }

        /// <summary>
        /// Gets all milestones belonging to a certain product.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="productID">Product id.</param>
        /// <returns>Array of milestones belonging to the product or null if it does not exist.</returns>
        public static IMilestone[] GetMilestonesByProductID(this IProcessProvider pp, string productID)
        {
            if (pp.GetProductByID(productID) == null)
                return null;

            List<IMilestone> milestones = new List<IMilestone>();
            foreach (IProcessElementDependency curDep in pp.Dependencies)
            {
                if (curDep is ProductMilestoneDependency)
                {
                    if ((curDep as ProductMilestoneDependency).ProductId.Equals(productID, StringComparison.OrdinalIgnoreCase))
                    {
                        IMilestone curMilestone = pp.GetMilestoneByID((curDep as ProductMilestoneDependency).MilestoneId);
                        if(curMilestone != null)
                            milestones.Add(curMilestone);
                    }
                }
            }
            return milestones.ToArray<IMilestone>();
        }

        /// <summary>
        /// Gets all products belonging to a certain milestone.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="milestoneID">Milestone id.</param>
        /// <returns>Array of products belonging to the milestone or null if it does not exist.</returns>
        public static IProduct[] GetProductsByMilestoneID(this IProcessProvider pp, string milestoneID)
        {
            if (pp.GetMilestoneByID(milestoneID) == null)
                return null;

            List<IProduct> products = new List<IProduct>();
            foreach (IProcessElementDependency curDep in pp.Dependencies)
            {
                if (curDep is ProductMilestoneDependency)
                {
                    if ((curDep as ProductMilestoneDependency).MilestoneId.Equals(milestoneID, StringComparison.OrdinalIgnoreCase))
                    {
                        IProduct curProduct = pp.GetProductByID((curDep as ProductMilestoneDependency).ProductId);
                        if (curProduct != null)
                            products.Add(curProduct);
                    }
                }
            }
            return products.ToArray<IProduct>();
        }


        /// <summary>
        /// Gets all activities belonging to a certain product.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="productID">Product id.</param>
        /// <returns>Array of activities belonging to the product or null if it does not exist.</returns>
        public static IActivity[] GetActivitiesByProductID(this IProcessProvider pp, string productID)
        {
            if (pp.GetProductByID(productID) == null)
                return null;

            List<IActivity> activities = new List<IActivity>();
            foreach (IProcessElementDependency curDep in pp.Dependencies)
            {
                if (curDep is ProductActivityDependency)
                {
                    if ((curDep as ProductActivityDependency).ProductId.Equals(productID, StringComparison.OrdinalIgnoreCase))
                    {
                        IActivity curActivity = pp.GetActivityByID((curDep as ProductActivityDependency).ActivityId);
                        if (curActivity != null)
                            activities.Add(curActivity);
                    }
                }
            }
            return activities.ToArray<IActivity>();
        }

        /// <summary>
        /// Gets all products belonging to a certain activity.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="activityID">Activity id.</param>
        /// <returns>Array of products belonging to the activity or null if it does not exist.</returns>
        public static IProduct[] GetProductsByActivityID(this IProcessProvider pp, string activityID)
        {
            if (pp.GetActivityByID(activityID) == null)
                return null;

            List<IProduct> products = new List<IProduct>();
            foreach (IProcessElementDependency curDep in pp.Dependencies)
            {
                if (curDep is ProductActivityDependency)
                {
                    if ((curDep as ProductActivityDependency).ActivityId.Equals(activityID, StringComparison.OrdinalIgnoreCase))
                    {
                        IProduct curProduct = pp.GetProductByID((curDep as ProductActivityDependency).ProductId);
                        if (curProduct != null)
                            products.Add(curProduct);
                    }
                }
            }
            return products.ToArray<IProduct>();
        }


        public static IProduct[] GetContentDependentProductsByProductID(this IProcessProvider pp, string productID)
        {
            return GetDependentProductsByProductID<ProductContentDependency>(pp, productID);
        }

        public static IProduct[] GetGeneratedProductsByProductID(this IProcessProvider pp, string productID)
        {
            return GetDependentProductsByProductID<GeneratingProductDependency>(pp, productID);
        }

        private static IProduct[] GetDependentProductsByProductID<T>(IProcessProvider pp, string productID) where T : ProductToProductDependency
        {
            if (pp.GetProductByID(productID) == null)
                return null;

            List<IProduct> products = new List<IProduct>();
            foreach (IProcessElementDependency curDep in pp.Dependencies)
            {
                if (curDep is T)
                {
                    T curProdDependency = curDep as T;
                    if (curProdDependency.ProductId.Equals(productID, StringComparison.OrdinalIgnoreCase))
                    {
                        IProduct prod = pp.GetProductByID(curProdDependency.DependentProductId);
                        if (prod != null)
                            products.Add(prod);
                    }
                }
            }
            return products.ToArray<IProduct>();
        }


        /// <summary>
        /// Gets all topics belonging to a certain product.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="productID">Product id.</param>
        /// <returns>Array of topics belonging to the product or null if it does not exist.</returns>
        public static ITopic[] GetTopicsByProductID(this IProcessProvider pp, string productID)
        {
            if (pp.GetProductByID(productID) == null)
                return null;

            List<OrderedTopic> topics = new List<OrderedTopic>();
            foreach (IProcessElementDependency curDep in pp.Dependencies)
            {
                if (curDep is ProductTopicDependency)
                {
                    ProductTopicDependency curPTDep = curDep as ProductTopicDependency;
                    if (curPTDep.ProductId.Equals(productID, StringComparison.OrdinalIgnoreCase))
                    {
                        ITopic curTopic = pp.GetTopicByID(curPTDep.TopicId);
                        if (curTopic != null)
                            topics.Add(new OrderedTopic() { Topic = curTopic, Order = curPTDep.Order });
                    }
                }
            }
            topics.Sort();

            ITopic[] topicsArr = new ITopic[topics.Count];
            for (int a = 0; a < topicsArr.Length; a++)
                topicsArr[a] = topics[a].Topic;
            return topicsArr;
        }
        private struct OrderedTopic : IComparable<OrderedTopic>
        {
            public ITopic Topic;
            public int Order;

            #region IComparable Members

            public int CompareTo(OrderedTopic obj)
            {
                return this.Order.CompareTo(obj.Order);
            }

            #endregion
        }

        /// <summary>
        /// Gets all products belonging to a certain topic.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="topicID">Topic id.</param>
        /// <returns>Array of products belonging to the topic or null if it does not exist.</returns>
        public static IProduct[] GetProductsByTopicID(this IProcessProvider pp, string topicID)
        {
            if (pp.GetTopicByID(topicID) == null)
                return null;

            List<IProduct> products = new List<IProduct>();
            foreach (IProcessElementDependency curDep in pp.Dependencies)
            {
                if (curDep is ProductTopicDependency)
                {
                    if ((curDep as ProductTopicDependency).TopicId.Equals(topicID, StringComparison.OrdinalIgnoreCase))
                    {
                        IProduct curProduct = pp.GetProductByID((curDep as ProductTopicDependency).ProductId);
                        if (curProduct != null)
                            products.Add(curProduct);
                    }
                }
            }
            return products.ToArray<IProduct>();
        }


        /// <summary>
        /// Gets all products belonging to a certain discipline.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="disciplineID">Discipline id.</param>
        /// <returns>Array of products belonging to the discipline or null if it does not exist.</returns>
        public static IProduct[] GetProductsByDisciplineID(this IProcessProvider pp, string disciplineID)
        {
            List<IProduct> productsList = new List<IProduct>();
            foreach (IProduct curProd in pp.Products)
            {
                if (curProd.DisciplineId.Equals(disciplineID, StringComparison.OrdinalIgnoreCase))
                    productsList.Add(curProd);
            }
            return productsList.ToArray();
        }
    }
}
